﻿#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC 
#endif

#ifdef _WIN32
#define HEAD EXTERNC __declspec(dllexport)
#define CallingConvention _cdecl
#else
#define HEAD EXTERNC
#define CallingConvention
#endif

#define FIELDOFFSET(TYPE, MEMBER) (int)(&(((TYPE*)0)->MEMBER))

#include <stdint.h>
#include "RCTP/RUDPEx.h"

typedef void(*recv_callback)(uint8_t* buffer, uint32_t len, sockaddr_in* remote, bool reliable, void* user);
recv_callback recvcb = nullptr;
typedef void(*send_callback)(Message* message, SendState state, void* user);
send_callback sendcb = nullptr;
typedef void(*errlog_callback)(const char* log);
errlog_callback errlogcb = nullptr;

HEAD void CallingConvention SetRecvCallback(recv_callback cb)
{
	recvcb = cb;
}

HEAD void CallingConvention SetSendCallback(send_callback cb)
{
	sendcb = cb;
}

HEAD void CallingConvention SetErrorLogCallback(errlog_callback cb)
{
	errlogcb = cb;
}

void LogCB(const char* log, void* user)
{
	if (errlogcb)
		errlogcb(log);
}
void RecvCB(uint8_t* buffer, uint32_t len, sockaddr_in* remote, bool reliable, void* user)
{
	if (recvcb)
		recvcb(buffer, len, remote, reliable, user);
}
void SendCB(Message* message, SendState state, void* user)
{
	if (sendcb)
		sendcb(message, state, user);
}

HEAD void* CallingConvention RUDPEx_New(void* user)
{
	RUDPEx* rudp = new RUDPEx();
	rudp->User = user;
	rudp->RegisterRecvCallback(&RecvCB);
	rudp->RegisterErrorLogCallback(&LogCB);
	rudp->RegisterSendCallback(&SendCB);
	return rudp;
}

HEAD void CallingConvention RUDPEx_Delete(void* rudp)
{
	if (!rudp)
		return;
	delete ((RUDPEx*)rudp);
}

HEAD int CallingConvention RUDPEx_Initialize(void* rudp, const char* ip, int port)
{
	if (!rudp)
		return 0;
	RUDP* r = (RUDP*)rudp;
	return r->Initialize(ip, port) ? 0 : -1;
}

HEAD int CallingConvention RUDPEx_GetLocalEndPoint(void* rudp, const char* ip, int* port)
{
	if (!rudp)
		return 0;
	RUDP* r = (RUDP*)rudp;
	char* oip;
	if (!r->GetLocalIP(&oip))
		return -1;
	memcpy((void*)ip, oip, strlen(oip));
	if (!r->GetLocalPort(port))
		return -1;
	return 0;
}

HEAD void CallingConvention RUDPEx_Terminate(void* rudp)
{
	if (!rudp)
		return;
	RUDP* r = (RUDP*)rudp;
	return r->Terminate();
}

HEAD bool CallingConvention RUDPEx_JoinMulticastGroup(void* rudp, const char* group)
{
	if (!rudp)
		return false;
	RUDP* r = (RUDP*)rudp;
	return r->JoinMulticastGroup(group) ? 0 : -1;
}

HEAD void CallingConvention RUDPEx_DropMulticastGroup(void* rudp)
{
	if (!rudp)
		return;
	RUDP* r = (RUDP*)rudp;
	r->DorpMulticastGroup();
}

HEAD void CallingConvention RUDPEx_Update(void* rudp, int timeout)
{
	if (!rudp)
		return;
	RUDP* r = (RUDP*)rudp;
	return r->Update(timeout);
}

HEAD void CallingConvention RUDPEx_Send(void* rudp, const char* buffer, int offset, int len, const char* ip, const int port, int reliable)
{
	if (!rudp)
		return;
	RUDP* r = (RUDP*)rudp;
	return r->SendTo((uint8_t*)buffer + offset, len, ip, port, reliable);
}

HEAD uint32_t CallingConvention RUDPEx_GetTimestamp()
{
	return RCTP::GetTimestamp();
}

HEAD void CallingConvention RUDPEx_SetOptionInt(void* rudp, int option, uint32_t value)
{
	if (!rudp)
		return;
	RUDP* r = (RUDP*)rudp;
	r->SetOption(option, value);
}

HEAD void CallingConvention RUDPEx_SetOptionFloat(void* rudp, int option, float value)
{
	if (!rudp)
		return;
	RUDP* r = (RUDP*)rudp;
	r->SetOption(option, value);
}